import {compareTwoVdom, createDOM} from '../react-dom';

export const updateQueue = {
  isBatchingUpdate: false, //当前是否处于批量更新模式, 默认值是false
  // updaters:new Set(), //因为是Set, 所以如果同一个组件多次setState, 只会存最后一次的setState的东东在这里(一个组件有且仅有一个Updater实例,setState时,批量更新模式下,会将这个updater实例add到这里) //但这里不知道为什么 for of 遍历不到 //Set遍历不是按照你加入的顺序来的(乱序)
  updaters:[],
  //批量更新
  batchUpdate(){
    for (const updater of this.updaters) {
      updater.updateComponent();
    }

    this.isBatchingUpdate = false;
    this.updaters.length = 0;
  }
}

//负责批量更新(处理多次调用setState的逻辑)
class Updater {
  constructor(classInstance) {
    this.classInstance = classInstance; //类组件的实例
    this.pendingStates = []; //等待生效的状态, 可能是一个对象, 也可能是一个函数
    this.callbacks = []; //this.setState(,cb) 传递的第二个参数
  }

  //Component setState调用
  addState(partialState,callback){
    this.pendingStates.push(partialState);

    (typeof callback === 'function') && this.callbacks.push(callback);

    this.emitUpdate();
  }

  // 不论属性(props)变了 还是 状态(state)变了 都会调用此方法
  // 除了上面 setState会间接调用此方法
  // dom-diff 深度比较 updateClassComponent 方法中也会调用此方法
  emitUpdate(nextProps/*←有值,说明是深度dom-diff时触发的,而不是setState*/){
    this.nextProps = (nextProps || this.classInstance.props);
    //如果当前是批量更新模式, 先存起来
    if(updateQueue.isBatchingUpdate){
      // updateQueue.updaters.add(this);
      if(!updateQueue.updaters.includes(this)) updateQueue.updaters.push(this);

    //不是批量更新
    //1. setState((lastState)=>{...})
    //2. setTimeout(()=>setState(),)
    }else {
      this.updateComponent(); //直接更新
    }
  }

  updateComponent(){
    // console.log('updateComponent');
    const {classInstance,pendingStates,callbacks,nextProps} = this;
    //如果有等待更新的状态对象
    if (nextProps || pendingStates.length > 0) {
      // classInstance.state = this.getState(); //计算新状态 更新老状态
      // classInstance.forceUpdate(callbacks);

      shouldUpdate(classInstance, nextProps, this.getState());
      callbacks.forEach(cb=>cb(classInstance.state))
      callbacks.length = 0;
    }
  }

  getState(){
    const {classInstance,pendingStates} = this;
    let {state} = classInstance;
    pendingStates.forEach((nextState)=>{
      if(typeof nextState === 'function'){
        //setState((lastState)=>{...})
        nextState = nextState.call(classInstance,state);
      }
      state = {...state, ...nextState}
    })
    pendingStates.length = 0; //这样清空 性能更高点 比 xx = []的方式 (因为省去了这里创建[]的事)

    return state;
  }
}

class Component {
  static isReactComponent = true; //用来区分是函数还是类组件
  constructor(props) {
    this.props = props || this.constructor.defaultProps;
    this.nextProps = null;
    this.state = {};
    this.updater = new Updater(this);
  }

  render(){
    throw new Error('此方法为抽象方法,需要子类实现');
  }

  setState(partialState,callback){
    /*const state = this.state;
    this.state = {...state, ...partialState};

    const newVdom = this.render();
    updateClassComponent(this,newVdom);*/
    this.updater.addState(partialState,callback);
  }

  forceUpdate(){
    const newRenderVdom = this.render();
    //updateClassComponent(this, newVdom); //← 这样是忽略了diff
    const currentRenderVdom = compareTwoVdom(this.oldRenderVdom.dom.parentNode, this.oldRenderVdom,  newRenderVdom);
    this.oldRenderVdom = currentRenderVdom;

    if(this.componentDidUpdate) this.componentDidUpdate();
  }
}

// ↓ 这样是忽略了diff
/*function updateClassComponent(/!*currentFiber*!/classInstance, newVdom) {
  const oldDOM = classInstance.dom; //在 mountClassComponent 里挂的
  const newDOM = createDOM(newVdom)
  oldDOM.parentNode.replaceChild(newDOM, oldDOM);
  classInstance.dom = newDOM;
}*/

function shouldUpdate(classInstance, nextProps, nextState){
  let willUpdate = true;

  if(
    classInstance.shouldComponentUpdate
    &&!classInstance.shouldComponentUpdate(nextProps,nextState)
  ){
    willUpdate = false; //shouldComponentUpdate返回结果 为false, 就不需要更新
  }

  willUpdate && classInstance.componentWillUpdate && classInstance.componentWillUpdate(nextProps, nextState);
  //↑componentWillUpdate执行时 状态还没有改变(通过this.state获取)

  if(nextProps){
    classInstance.props = nextProps;
  }
  classInstance.state = nextState; //不论组件是否要刷新 都要更新state

  willUpdate && classInstance.forceUpdate();
}

export default Component;
